package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric-contract-api-go/contractapi"
	"log"
	"math/rand"
	"time"
)

// SmartContract provides functions for managing an Asset

type SmartContract struct {
	contractapi.Contract
}

// Log Asset describes basic details of what makes up a simple asset

type Log struct {
	Owner      string `json:"PK_DS"`
	Controller string `json:"PK_DC"`
	Processor  string `json:"PK_DP"`
	Token      int32  `json:"token"`
	Status     string `json:"status"`
	Operation  string `json:"operation"`
	Expiration int    `json:"expiration"`
	Time       string `json:"ts"`
	//Policy     string `json:"policy"`
}

// InitLedger adds a base set of assets to the ledger

func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {

	//log1.Token = string(rand.Int())
	t := time.Now() //当前时间
	timeLayoutStr := "2006-01-02 15:04:05"
	//log1.Time = t.Format(timeLayoutStr)
	//policy1 := []string{"add", "update"}
	//policyasbytes, _ := json.Marshal(policy1)
	assets := []Log{
		{Owner: "001", Controller: "002", Processor: "003", Token: rand.Int31(), Status: "approved", Operation: "add", Expiration: 3600, Time: t.Format(timeLayoutStr)},
		{Owner: "004", Controller: "002", Processor: "003", Token: rand.Int31(), Status: "approved", Operation: "add", Expiration: 3600, Time: t.Format(timeLayoutStr)},
		{Owner: "005", Controller: "002", Processor: "003", Token: rand.Int31(), Status: "approved", Operation: "add", Expiration: 3600, Time: t.Format(timeLayoutStr)},
		{Owner: "006", Controller: "002", Processor: "003", Token: rand.Int31(), Status: "approved", Operation: "add", Expiration: 3600, Time: t.Format(timeLayoutStr)},
		{Owner: "007", Controller: "002", Processor: "003", Token: rand.Int31(), Status: "approved", Operation: "add", Expiration: 3600, Time: t.Format(timeLayoutStr)},
		{Owner: "008", Controller: "002", Processor: "003", Token: rand.Int31(), Status: "approved", Operation: "add", Expiration: 3600, Time: t.Format(timeLayoutStr)},
	}
	log1 := Log{}
	log1.Owner = "111"
	log1.Processor = "222"
	log1.Controller = "333"
	//log1.Policy = string(policyasbytes)
	assets = append(assets, log1)

	//err1 = ctx.GetStub().
	for _, asset := range assets {
		assetJSON, err := json.Marshal(asset)
		if err != nil {
			return err
		}
		//err = ctx.GetStub().
		err = ctx.GetStub().PutState(asset.Owner+asset.Controller+asset.Processor, assetJSON)
		if err != nil {
			return fmt.Errorf("failed to put to world state. %v", err)
		}
	}

	return nil
}

//func (s *SmartContract) GrantConsent(ctx contractapi.TransactionContextInterface, owner string, controller string, processor string, permission string) error {
//	assets := []Log{
//		{Owner: "001", Controller: "002", Processor: "003", Token: "xxx111", Status: "approved", Activity: "blue", Expiration: 3600, Time: "12321312"},
//		{Owner: "004", Controller: "002", Processor: "003", Token: "xxx111", Status: "approved", Activity: "blue", Expiration: 3600, Time: "12321312"},
//		{Owner: "005", Controller: "002", Processor: "003", Token: "xxx111", Status: "approved", Activity: "blue", Expiration: 3600, Time: "12321312"},
//		{Owner: "006", Controller: "002", Processor: "003", Token: "xxx111", Status: "approved", Activity: "blue", Expiration: 3600, Time: "12321312"},
//		{Owner: "007", Controller: "002", Processor: "003", Token: "xxx111", Status: "approved", Activity: "blue", Expiration: 3600, Time: "12321312"},
//		{Owner: "008", Controller: "002", Processor: "003", Token: "xxx111", Status: "approved", Activity: "blue", Expiration: 3600, Time: "12321312"},
//	}
//	//err1 = ctx.GetStub().
//	for _, asset := range assets {
//		assetJSON, err := json.Marshal(asset)
//		if err != nil {
//			return err
//		}
//		//err = ctx.GetStub().
//		err = ctx.GetStub().PutState(asset.Owner+asset.Controller+asset.Processor, assetJSON)
//		if err != nil {
//			return fmt.Errorf("failed to put to world state. %v", err)
//		}
//	}
//
//	return nil
//}

// QueryLedger returns the asset stored in the world state with given id.

//func (s *SmartContract) QueryLedger(ctx contractapi.TransactionContextInterface, id string) (*Log, error) {
//	//log1 := LogLedger{key: key{owner: "004", controller: "002", processor: "003"}, value: value{Token: "xxx111", Status: "approved", Activity: "blue", Expiration: 3600, Timestm: "12321312"}}
//	//args := stub.GetStringArgs()
//	//log1AsBytes, _ := json.Marshal(log1)
//	//PIstub.PutState(log1.key.owner+log1.key.controller+log1.key.processor, log1AsBytes)
//	value, err := ctx.GetStub().GetState(id)
//	if err != nil {
//		return nil, fmt.Errorf("invalid Smart Contract function name")
//	}
//	if value == nil {
//		return nil, fmt.Errorf("asset not found")
//	}
//	//fmt.Println("Added", log1)
//	var log1 Log
//	err = json.Unmarshal(value, &log1)
//	if err != nil {
//		return nil, err
//	}
//	var po = log1.Policy
//
//	var po1 = []string{}
//
//	err = json.Unmarshal([]byte(po), po1)
//	return &log1, nil
//	//return string(value), nil
//}

// QueryLedger returns the asset stored in the world state with given id.

//func (s *SmartContract) QueryLedger1(ctx contractapi.TransactionContextInterface, id string) string {
//	//log1 := LogLedger{key: key{owner: "004", controller: "002", processor: "003"}, value: value{Token: "xxx111", Status: "approved", Activity: "blue", Expiration: 3600, Timestm: "12321312"}}
//	//args := stub.GetStringArgs()
//	//log1AsBytes, _ := json.Marshal(log1)
//	//PIstub.PutState(log1.key.owner+log1.key.controller+log1.key.processor, log1AsBytes)
//	value, err := ctx.GetStub().GetState(id)
//	if err != nil {
//		return ("invalid Smart Contract function name")
//	}
//	if value == nil {
//		return ("asset not found")
//	}
//	//fmt.Println("Added", log1)
//
//	log1 := Log{}
//	err = json.Unmarshal(value, &log1)
//	if err != nil {
//		return ("No log")
//	}
//	po := log1.Policy
//
//	po1 := []string{}
//
//	err = json.Unmarshal([]byte(po), &po1)
//	//ctx.GetStub().DelState()
//	return po1[0]
//	//return string(value), nil
//}

func (s *SmartContract) GrantConsent(ctx contractapi.TransactionContextInterface, owner string, controller string, processor string, op string) string {
	params := []string{"GrantConsent", owner, controller, op}
	queryArgs := make([][]byte, len(params))
	for i, arg := range params {
		queryArgs[i] = []byte(arg)
	}

	response := ctx.GetStub().InvokeChaincode("3a_cc", queryArgs, "mychannel")
	log1 := new(Log)

	log1.Owner = owner
	log1.Controller = controller
	log1.Status = "approved"
	log1.Processor = processor
	log1.Expiration = rand.Int()
	log1.Token = rand.Int31()
	t := time.Now() //当前时间
	timeLayoutStr := "2006-01-02 15:04:05"
	log1.Time = t.Format(timeLayoutStr)
	logAsBytes, _ := json.Marshal(log1)
	//fmt.Println("Added", log1)

	_ = ctx.GetStub().PutState(log1.Owner+log1.Controller+log1.Processor, logAsBytes)
	return string(response.Payload)
}

func (s *SmartContract) RevokeConsent(ctx contractapi.TransactionContextInterface, owner string, controller string, processor string, op string) string {
	params := []string{"RevokeConsent", owner, controller, op}
	queryArgs := make([][]byte, len(params))
	for i, arg := range params {
		queryArgs[i] = []byte(arg)
	}

	response := ctx.GetStub().InvokeChaincode("3a_cc", queryArgs, "mychannel")
	log1 := new(Log)

	log1.Owner = owner
	log1.Controller = controller
	log1.Status = "approved"
	log1.Processor = processor
	log1.Expiration = rand.Int()
	log1.Token = rand.Int31()
	t := time.Now() //当前时间
	timeLayoutStr := "2006-01-02 15:04:05"
	log1.Time = t.Format(timeLayoutStr)
	logAsBytes, _ := json.Marshal(log1)
	//fmt.Println("Added", log1)

	_ = ctx.GetStub().PutState(log1.Owner+log1.Controller+log1.Processor, logAsBytes)
	return string(response.Payload)
}

func (s *SmartContract) DataAccess(ctx contractapi.TransactionContextInterface, owner string, controller string, processor string, op string) int32 {
	params := []string{"DateAccess", owner, controller, op}
	queryArgs := make([][]byte, len(params))
	for i, arg := range params {
		queryArgs[i] = []byte(arg)
	}

	response := ctx.GetStub().InvokeChaincode("3a_cc", queryArgs, "mychannel")
	if string(response.Payload) == "success" {
		log1 := new(Log)
		value, _ := ctx.GetStub().GetState(owner + controller + processor)
		_ = json.Unmarshal(value, &log1)
		return log1.Token
	} else {
		return -1
	}

	//log1 := new(Log)
	//
	//log1.Owner = owner
	//log1.Controller = controller
	//log1.Status = "approved"
	//log1.Processor = processor
	//log1.Expiration = rand.Int()
	//log1.Token = rand.Int31()
	//t := time.Now() //当前时间
	//timeLayoutStr := "2006-01-02 15:04:05"
	//log1.Time = t.Format(timeLayoutStr)
	//logAsBytes, _ := json.Marshal(log1)
	////fmt.Println("Added", log1)
	//
	//_ = ctx.GetStub().PutState(log1.Owner+log1.Controller+log1.Processor, logAsBytes)
	//return string(response.Payload)
}

func (s *SmartContract) TokenValidation(ctx contractapi.TransactionContextInterface, pk string, token int32, op string) string {
	//token := args[0]
	queryString := fmt.Sprintf(`{"selector":{"token":%v}}`, token)    
	resultsIterator, err := ctx.GetStub().GetQueryResult(queryString) 

	if err != nil {
		return "Rich query failed"
	}
	defer resultsIterator.Close() //释放迭代器

	var buffer bytes.Buffer
	bArrayMemberAlreadyWritten := false
	buffer.WriteString(`{"result":[`)

	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next() //获取迭代器中的每一个值
		if err != nil {
			return "Fail"
		}
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString(",")
		}
		buffer.WriteString(string(queryResponse.Value)) //将查询结果放入Buffer中
		log1 := new(Log)

		_ = json.Unmarshal(queryResponse.Value, &log1)
		if pk == log1.Owner || pk == log1.Controller {
			return "accepted"
		} else if op == log1.Operation && log1.Status == "approved" && log1.Processor == pk {
			return "accepted1"
		}
		//return log1.Owner
		bArrayMemberAlreadyWritten = true
	}
	buffer.WriteString(`]}`)
	fmt.Printf("Query result: %s", buffer.String())

	return string(buffer.Bytes())

}

// AddLedger issues a new asset to the world state with given details.

func (s *SmartContract) AddLedger(ctx contractapi.TransactionContextInterface, owner string, controller string, processor string, token int32, status string, activity string) error {
	log1 := new(Log)

	log1.Owner = owner
	log1.Controller = controller

	log1.Processor = processor
	log1.Token = token
	log1.Status = status
	log1.Operation = activity
	t := time.Now() //当前时间
	timeLayoutStr := "2006-01-02 15:04:05"
	log1.Time = t.Format(timeLayoutStr)

	logAsBytes, _ := json.Marshal(log1)
	//err =
	//value, err := APIstub.GetState(log1.key.owner + log1.key.controller + log1.key.processor)
	//if err != nil {
	//	return shim.Error("Invalid Smart Contract function name.")
	//}
	// if value == nil {
	// 	return shim.Error("Asset not found:")
	// }
	fmt.Println("Added", log1)

	return ctx.GetStub().PutState(log1.Owner+log1.Controller+log1.Processor, logAsBytes)
}

func (s *SmartContract) DeleteLedger(ctx contractapi.TransactionContextInterface, key string) error {

	// Delete the key from the state in ledger
	err := ctx.GetStub().DelState(key)
	if err != nil {
		return fmt.Errorf("failed to delete state")
	}

	return nil
}

func main() {
	assetChaincode, err := contractapi.NewChaincode(&SmartContract{})
	if err != nil {
		log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
	}

	if err := assetChaincode.Start(); err != nil {
		log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
	}
}
